package com.gastudio.Protocol;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

/**
 * 解码器【字节流转换成对象】
 * Created by 余艾龙 on 2017/6/21.
 */
public class ProtocolDecoder implements org.apache.mina.filter.codec.ProtocolDecoder {
    //为解决半包问题，设置一个AttributeKey对象保存上一次传入包的内容
    private final AttributeKey CONTEXT=new AttributeKey(this.getClass(),"context");
    private final Charset charset;
    private int maxPackLength=1024;//设置包的最大长度，如果超过这个长度那么这个包可能就会有问题

    public ProtocolDecoder(){
        this(Charset.defaultCharset());
    }

    public ProtocolDecoder(Charset charset) {
        this.charset = charset;
    }

    public Context getContext(IoSession session){
        Context ctx=(Context)session.getAttribute(CONTEXT);
        if(ctx==null){
            ctx=new Context();
            session.setAttribute(CONTEXT,ctx);
        }
        return ctx;
    }

    @Override
    public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
        final  int packHeadLength=5;//定义包头长度
        Context ctx=this.getContext(session);
        ctx.append(in);
        IoBuffer buf=ctx.getBuf();
        buf.flip();//缓冲区的指针从0开始
        while (buf.remaining()>=packHeadLength){//传入buf的内容必须大于包头长度
            buf.mark();
            int length=buf.getInt();
            byte flag=buf.get();
            if(length<0||length>maxPackLength){//检查报文长度比0小，或者比包的最大长度还大,无效包放弃
                buf.reset();
                break;
            }else if(length>=packHeadLength&&length-packHeadLength<=buf.remaining()){//数据正常情况
                int oldLimit=buf.limit();
                buf.limit(buf.position()+length-packHeadLength);
                String content=buf.getString(ctx.getDecoder());
                buf.limit(oldLimit);
                ProtocolPack pack=new ProtocolPack(flag,content);
                out.write(pack);
            }else{
                //半包情况,将半包放入缓冲区，下次一起读出
                buf.clear();
                break;
            }
        }
        if(buf.hasRemaining()){//读完之后是否缓冲区中是否还有数据
            IoBuffer temp=IoBuffer.allocate(maxPackLength).setAutoExpand(true);
            temp.put(buf);
            temp.flip();
            buf.reset();
            buf.put(temp);
        }else{
            buf.reset();
        }
    }

    @Override
    public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {

    }

    @Override
    public void dispose(IoSession session) throws Exception {
        Context ctx=(Context)session.getAttribute(CONTEXT);
        if(ctx!=null){
            session.removeAttribute(CONTEXT);
        }
    }

    //内部类
    private class Context{
        public CharsetDecoder getDecoder() {
            return decoder;
        }

        public IoBuffer getBuf() {
            return buf;
        }

        public void setBuf(IoBuffer buf) {
            this.buf = buf;
        }

        private final CharsetDecoder decoder;
        private IoBuffer buf;
        private Context() {
            this.decoder = charset.newDecoder();
            buf=IoBuffer.allocate(80).setAutoExpand(true);
        }
        //最加上一次缓冲区的数据
        public void append(IoBuffer inBuf){
            this.getBuf().put(inBuf);
        }

        public void reset(){
            decoder.reset();
        }
    }

    public int getMaxPackLength() {
        return maxPackLength;
    }

    public void setMaxPackLength(int maxPackLength) {
        if(maxPackLength<0){
            throw new IllegalArgumentException("maxPackLength参数错误："+maxPackLength);
        }
        this.maxPackLength = maxPackLength;
    }
}
